﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Ibp.Service.Entity.DEntity;
using Ibp.Service.Entity.ODAEntity;
using OdaOperation.Core.Simplifier;
using OdaOperation.DB;
using OdaOperation.Model;
using OdaOperation.Utils;
using Teigha.Core;
using Teigha.TD;

namespace OdaOperation.Extends
{
    public static class ExpandQueryDbDatabase
    {
        // 扩展方法：通过名称获取块表记录
        public static OdDbBlockTableRecord GetBlockByName(this OdDbDatabase database, string name)
        {
            // 打开块表
            OdDbBlockTable odDbBlockTable = database.getBlockTableId().openObject() as OdDbBlockTable;

            // 检查块表中是否存在指定名称的块
            if (odDbBlockTable.has(name))
            {
                // 返回块表记录
                return odDbBlockTable.getAt(name).openObject() as OdDbBlockTableRecord;
            }

            // 如果没有找到，返回null
            return null;
        }

        // 扩展方法：获取所有层信息
        public static IEnumerable<DLayer> GetLayers(this DbDatabase database)
        {
            // 打开层表
            OdDbLayerTable odDbLayerTable = database.Database.getLayerTableId().openObject() as OdDbLayerTable;
            // 创建层表迭代器
            OdDbSymbolTableIterator pIter = odDbLayerTable.newIterator();
            pIter.start();
            // 遍历层表
            while (!pIter.done())
            {
                // 获取层表记录
                OdDbLayerTableRecord odDbLayerTableRecord = (OdDbLayerTableRecord)pIter.getRecordId().safeOpenObject();
                // 创建新的层对象
                DLayer val = new DLayer();
                // 设置层的属性
                val.Id = (odDbLayerTableRecord.objectId().getHandle().ToUInt64());
                val.Color = ($"{odDbLayerTableRecord.color().red()}, {odDbLayerTableRecord.color().green()},{odDbLayerTableRecord.color().blue()}");
                val.Name = (odDbLayerTableRecord.getName());
                val.LineTypeId = (odDbLayerTableRecord.linetypeObjectId().getHandle().ToUInt64());
                // 返回层对象
                yield return val;
                pIter.step();
            }
        }

        // 私有方法：添加项目到列表中
        private static void AddItem<T, V>(List<T> list, OdGiDrawable drawable, OdGeMatrix3d transform, string layername, ulong layerId, List<ExportDump> path) where T : DEntityBase, new() where V : OdDbEntity
        {
            // 将可绘制对象转换为指定类型
            V val = drawable as V;
            if (val != null)
            {
                // 变换实体并添加到列表中
                OdResult result;
                T item = TransformEntity(val, transform, out result).ToDEntity<T>(layername, layerId, path);
                list.Add(item);
            }
        }

        // 扩展方法：获取剪切数据
        public static void GetCutData(this DbDatabase database, ref DbOutData data)
        {
            // 打开模型空间块表记录
            OdDbBlockTableRecord odDbBlockTableRecord = database.Database.getModelSpaceId().openObject() as OdDbBlockTableRecord;

            // 获取所有块引用的外接矩形
            List<OdGeExtents3d> list2 = (from x in odDbBlockTableRecord.SelectEntity().OfType<OdDbBlockReference>().Select(delegate (OdDbBlockReference x)
            {
                OdGeExtents3d odGeExtents3d = new OdGeExtents3d();
                OdGiClipBoundary odGiClipBoundary = new OdGiClipBoundary();
                // 获取空间过滤器
                OdDbSpatialFilter odDbSpatialFilter = TD_Db.getFilter(x, OdDbSpatialFilter.desc(), OpenMode.kForRead) as OdDbSpatialFilter;
                if (odDbSpatialFilter != null)
                {
                    // 获取过滤器定义并转换外接矩形
                    odDbSpatialFilter.getDefinition(odGiClipBoundary, out var _);
                    foreach (OdGePoint3d item2 in odGiClipBoundary.m_Points.Select((OdGePoint2d p) => new OdGePoint3d(p.x, p.y, 0.0)))
                    {
                        odGeExtents3d.addPoint(item2);
                    }

                    odGeExtents3d.transformBy(x.blockTransform());
                    return odGeExtents3d;
                }

                // 获取内部块表记录并递归计算外接矩形
                OdDbBlockTableRecord odDbBlockTableRecord2 = x.blockTableRecord().openObject() as OdDbBlockTableRecord;
                if (odDbBlockTableRecord2 != null)
                {
                    List<OdGeExtents3d> list3 = (from y in odDbBlockTableRecord2.SelectEntity().OfType<OdDbBlockReference>().Select(delegate (OdDbBlockReference y)
                    {
                        OdGeExtents3d odGeExtents3d3 = new OdGeExtents3d();
                        OdGiClipBoundary odGiClipBoundary2 = new OdGiClipBoundary();
                        OdDbSpatialFilter odDbSpatialFilter2 = TD_Db.getFilter(y, OdDbSpatialFilter.desc(), OpenMode.kForRead) as OdDbSpatialFilter;
                        if (odDbSpatialFilter2 != null)
                        {
                            odDbSpatialFilter2.getDefinition(odGiClipBoundary2, out var _);
                            foreach (OdGePoint3d item3 in odGiClipBoundary2.m_Points.Select((OdGePoint2d p) => new OdGePoint3d(p.x, p.y, 0.0)))
                            {
                                odGeExtents3d3.addPoint(item3);
                            }

                            odGeExtents3d3.transformBy(y.blockTransform());
                            return odGeExtents3d3;
                        }

                        return null;
                    })
                                                 where y != null
                                                 select y).ToList();
                    OdGeExtents3d odGeExtents3d2 = new OdGeExtents3d();
                    foreach (OdGeExtents3d item4 in list3)
                    {
                        odGeExtents3d2.addExt(item4);
                    }

                    odGeExtents3d2.transformBy(x.blockTransform());
                    return odGeExtents3d2;
                }

                return null;
            })
                                         where x != null
                                         select x).ToList();
            OdGeExtents3d box = new OdGeExtents3d();
            foreach (OdGeExtents3d item5 in list2)
            {
                box.addExt(item5);
            }

            // 获取外接矩形中心
            OdGePoint3d point = box.center();
            if (!box.isValidExtents())
            {
                odDbBlockTableRecord.getGeomExtents(box);
            }
            else
            {
                box.addPoint(new OdGePoint3d(point).Add(new OdGeVector3d(0.0, 0.0, -2000.0)));
                box.addPoint(new OdGePoint3d(point).Add(new OdGeVector3d(0.0, 0.0, 2000.0)));
            }

            MemoryManager memoryManager = MemoryManager.GetMemoryManager();
            MemoryTransaction value = memoryManager.StartTransaction();
            try
            {
                // 定义输出列表
                List<DArc> outArcs = new List<DArc>();
                List<DEllipse> outEllipses = new List<DEllipse>();
                List<DLine> outLines = new List<DLine>();
                List<DPolyline> outPolylines = new List<DPolyline>();
                List<DPoint> outPoints = new List<DPoint>();
                List<DSpline> outSplines = new List<DSpline>();
                List<DText> outTexts = new List<DText>();
                DbOutOption options = data.Option;
                Dictionary<string, string> dicUnHandledTypes = new Dictionary<string, string>();
                List<ExportDump> list = new List<ExportDump>();
                OdGeExtents3d extents3D = new OdGeExtents3d();

                // 遍历所有可绘制对象
                new Core.Simplifier.Dumper(database.Database).Traverse(delegate (OdGiDrawable drawable, OdGeMatrix3d transform)
                {
                    int num = -1;
                    for (int i = 0; i < list.Count; i++)
                    {
                        if (list[i].ClassName == "AcDbBlockReference" && list[i].LayerName != "0")
                        {
                            num = i;
                        }
                    }

                    OdDbEntity odDbEntity2 = OdDbEntity.cast(drawable);
                    if (odDbEntity2 != null)
                    {
                        list.Add(ExportDump.From(odDbEntity2));
                    }

                    drawable.getGeomExtents(extents3D);
                    extents3D.transformBy(transform);
                    OdGeExtents3d.IntersectionStatus intersectionStatus = extents3D.intersectWith(box);
                    if (intersectionStatus == OdGeExtents3d.IntersectionStatus.kIntersectNot || intersectionStatus == OdGeExtents3d.IntersectionStatus.kIntersectOpIn)
                    {
                        return true;
                    }

                    string refLayerName = "";
                    ulong refLayerId = 0uL;
                    if (odDbEntity2 != null)
                    {
                        if (odDbEntity2.layer() == "0" && num != -1)
                        {
                            refLayerName = list[num].LayerName;
                            refLayerId = list[num].LayerId;
                        }
                        else
                        {
                            refLayerName = odDbEntity2.layer();
                            refLayerId = odDbEntity2.layerId().getHandle().ToUInt64();
                        }
                    }

                    // 获取处理选项并调用对应的方法
                    OdResult result;
                    if (options.Line)
                    {
                        OdDbLine odDbLine = drawable as OdDbLine;
                        if (odDbLine != null)
                        {
                            DLine item = Common.Spilt(box, TransformEntity(odDbLine, transform, out result)).ToDLine(refLayerName, refLayerId, list);
                            outLines.Add(item);
                            return true;
                        }
                    }
                    if (options.Arc)
                    {
                        OdDbArc odDbArc = drawable as OdDbArc;
                        if (odDbArc != null)
                        {
                            outArcs.Add(TransformEntity(odDbArc, transform, out result).ToDArc(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Ellipse)
                    {
                        OdDbEllipse odDbEllipse = drawable as OdDbEllipse;
                        if (odDbEllipse != null)
                        {
                            outEllipses.Add(TransformEntity(odDbEllipse, transform, out result).ToDEllipse(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Circle)
                    {
                        OdDbCircle odDbCircle = drawable as OdDbCircle;
                        if (odDbCircle != null)
                        {
                            outArcs.Add(TransformEntity(odDbCircle, transform, out result).ToDArc(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Point)
                    {
                        OdDbPoint odDbPoint = drawable as OdDbPoint;
                        if (odDbPoint != null)
                        {
                            outPoints.Add(odDbPoint.ToDPoint());
                            return true;
                        }
                    }

                    if (options.Polyline)
                    {
                        OdDb3dPolyline odDb3dPolyline = drawable as OdDb3dPolyline;
                        if (odDb3dPolyline != null)
                        {
                            OdResult result2;
                            DPolyline val = TransformEntity(odDb3dPolyline, transform, out result2).ToDPloyLine(refLayerName, refLayerId, transform, result2, list);
                            if (BoxContains(box, val))
                            {
                                outPolylines.Add(val);
                            }

                            return true;
                        }
                    }

                    if (options.Polyline)
                    {
                        OdDb2dPolyline odDb2dPolyline = drawable as OdDb2dPolyline;
                        if (odDb2dPolyline != null)
                        {
                            OdResult result3;
                            DPolyline val2 = TransformEntity(odDb2dPolyline, transform, out result3).ToDPloyLine(refLayerName, refLayerId, transform, result3, list);
                            if (BoxContains(box, val2))
                            {
                                outPolylines.Add(val2);
                            }

                            return true;
                        }
                    }

                    if (options.Polyline)
                    {
                        OdDbPolyline odDbPolyline = drawable as OdDbPolyline;
                        if (odDbPolyline != null)
                        {
                            OdResult result4;
                            DPolyline val3 = TransformEntity(odDbPolyline, transform, out result4).ToDPloyLine(refLayerName, refLayerId, transform, result4, list);
                            if (BoxContains(box, val3))
                            {
                                outPolylines.Add(val3);
                            }

                            return true;
                        }
                    }

                    if (options.Spline)
                    {
                        OdDbSpline odDbSpline = drawable as OdDbSpline;
                        if (odDbSpline != null)
                        {
                            outSplines.Add(TransformEntity(odDbSpline, transform, out result).ToDSpline(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Text)
                    {
                        OdDbText odDbText = drawable as OdDbText;
                        if (odDbText != null)
                        {
                            outTexts.Add(TransformEntity(odDbText, transform, out result).ToDText(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Text)
                    {
                        OdDbMText odDbMText = drawable as OdDbMText;
                        if (odDbMText != null)
                        {
                            outTexts.Add(TransformEntity(odDbMText, transform, out result).ToDText(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (drawable is OdDbBlockTableRecord || drawable is OdDbBlockReference || drawable is OdDbDimension)
                    {
                        return true;
                    }

                    string name = drawable.GetType().Name;
                    if (!dicUnHandledTypes.ContainsKey(name))
                    {
                        dicUnHandledTypes.Add(name, name);
                    }

                    return true;
                }, delegate (OdGiDrawable drawable, OdGeMatrix3d rsn)
                {
                    OdDbEntity odDbEntity = OdDbEntity.cast(drawable);
                    if (odDbEntity != null)
                    {
                        list.Remove(list.LastOrDefault());
                    }

                    return true;
                });
                string text = string.Join(" ", dicUnHandledTypes.Keys);
                Console.WriteLine("UnHandled types: " + text);
                data.Arcs.AddRange(outArcs);
                data.Ellipses.AddRange(outEllipses);
                data.Lines.AddRange(outLines);
                data.Points.AddRange(outPoints);
                data.Polylines.AddRange(outPolylines);
                data.Splines.AddRange(outSplines);
                data.Texts.AddRange(outTexts);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                memoryManager.StopTransaction(value);
            }
        }

        private static bool BoxContains(OdGeExtents3d box, DPolyline polyline)
        {
            bool result = false;
            foreach (DVertex vertex in polyline.Vertexes)
            {
                if (box.contains(new OdGePoint3d(((DPoint)vertex).X, ((DPoint)vertex).Y, ((DPoint)vertex).Z), new OdGeTol()))
                {
                    result = true;
                }
            }

            return result;
        }

        public static void GetData(this DbDatabase database, ref DbOutData data)
        {
            MemoryManager memoryManager = MemoryManager.GetMemoryManager();
            MemoryTransaction value = memoryManager.StartTransaction();
            try
            {
                List<DArc> outArcs = new List<DArc>();
                List<DEllipse> outEllipses = new List<DEllipse>();
                List<DLine> outLines = new List<DLine>();
                List<DPolyline> outPolylines = new List<DPolyline>();
                List<DPoint> outPoints = new List<DPoint>();
                List<DSpline> outSplines = new List<DSpline>();
                List<DText> outTexts = new List<DText>();
                DbOutOption options = data.Option;
                Dictionary<string, string> dicUnHandledTypes = new Dictionary<string, string>();
                List<ExportDump> list = new List<ExportDump>();
                new Core.Simplifier.Dumper(database.Database).Traverse(delegate (OdGiDrawable drawable, OdGeMatrix3d transform)
                {
                    int num = -1;
                    for (int i = 0; i < list.Count; i++)
                    {
                        if (list[i].ClassName == "AcDbBlockReference" && list[i].LayerName != "0")
                        {
                            num = i;
                        }
                    }

                    OdDbEntity odDbEntity2 = OdDbEntity.cast(drawable);
                    if (odDbEntity2 != null)
                    {
                        list.Add(ExportDump.From(odDbEntity2));
                    }

                    string refLayerName = "";
                    ulong refLayerId = 0uL;
                    if (odDbEntity2 != null)
                    {
                        if (odDbEntity2.layer() == "0" && num != -1)
                        {
                            refLayerName = list[num].LayerName;
                            refLayerId = list[num].LayerId;
                        }
                        else
                        {
                            refLayerName = odDbEntity2.layer();
                            refLayerId = odDbEntity2.layerId().getHandle().ToUInt64();
                        }
                    }

                    OdResult result;
                    if (options.Line)
                    {
                        OdDbLine odDbLine = drawable as OdDbLine;
                        if (odDbLine != null)
                        {
                            DLine item = TransformEntity(odDbLine, transform, out result).ToDLine(refLayerName, refLayerId, list);
                            outLines.Add(item);
                            return true;
                        }
                    }

                    if (options.Arc)
                    {
                        OdDbArc odDbArc = drawable as OdDbArc;
                        if (odDbArc != null)
                        {
                            outArcs.Add(TransformEntity(odDbArc, transform, out result).ToDArc(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Ellipse)
                    {
                        OdDbEllipse odDbEllipse = drawable as OdDbEllipse;
                        if (odDbEllipse != null)
                        {
                            outEllipses.Add(TransformEntity(odDbEllipse, transform, out result).ToDEllipse(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Circle)
                    {
                        OdDbCircle odDbCircle = drawable as OdDbCircle;
                        if (odDbCircle != null)
                        {
                            outArcs.Add(TransformEntity(odDbCircle, transform, out result).ToDArc(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Point)
                    {
                        OdDbPoint odDbPoint = drawable as OdDbPoint;
                        if (odDbPoint != null)
                        {
                            outPoints.Add(odDbPoint.ToDPoint());
                            return true;
                        }
                    }

                    if (options.Polyline)
                    {
                        OdDb3dPolyline odDb3dPolyline = drawable as OdDb3dPolyline;
                        if (odDb3dPolyline != null)
                        {
                            outPolylines.Add(TransformEntity(odDb3dPolyline, transform, out var result2).ToDPloyLine(refLayerName, refLayerId, transform, result2, list));
                            return true;
                        }
                    }

                    if (options.Polyline)
                    {
                        OdDb2dPolyline odDb2dPolyline = drawable as OdDb2dPolyline;
                        if (odDb2dPolyline != null)
                        {
                            outPolylines.Add(TransformEntity(odDb2dPolyline, transform, out var result3).ToDPloyLine(refLayerName, refLayerId, transform, result3, list));
                            return true;
                        }
                    }

                    if (options.Polyline)
                    {
                        OdDbPolyline odDbPolyline = drawable as OdDbPolyline;
                        if (odDbPolyline != null)
                        {
                            outPolylines.Add(TransformEntity(odDbPolyline, transform, out var result4).ToDPloyLine(refLayerName, refLayerId, transform, result4, list));
                            return true;
                        }
                    }

                    if (options.Spline)
                    {
                        OdDbSpline odDbSpline = drawable as OdDbSpline;
                        if (odDbSpline != null)
                        {
                            outSplines.Add(TransformEntity(odDbSpline, transform, out result).ToDSpline(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Text)
                    {
                        OdDbText odDbText = drawable as OdDbText;
                        if (odDbText != null)
                        {
                            outTexts.Add(TransformEntity(odDbText, transform, out result).ToDText(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (options.Text)
                    {
                        OdDbMText odDbMText = drawable as OdDbMText;
                        if (odDbMText != null)
                        {
                            outTexts.Add(TransformEntity(odDbMText, transform, out result).ToDText(refLayerName, refLayerId, list));
                            return true;
                        }
                    }

                    if (drawable is OdDbBlockTableRecord || drawable is OdDbBlockReference || drawable is OdDbDimension)
                    {
                        return true;
                    }

                    string name = drawable.GetType().Name;
                    if (!dicUnHandledTypes.ContainsKey(name))
                    {
                        dicUnHandledTypes.Add(name, name);
                    }

                    return true;
                }, delegate (OdGiDrawable drawable, OdGeMatrix3d rsn)
                {
                    OdDbEntity odDbEntity = OdDbEntity.cast(drawable);
                    if (odDbEntity != null)
                    {
                        list.Remove(list.LastOrDefault());
                    }

                    return true;
                });
                string text = string.Join(" ", dicUnHandledTypes.Keys);
                Console.WriteLine("UnHandled types: " + text);
                data.Arcs.AddRange(outArcs);
                data.Ellipses.AddRange(outEllipses);
                data.Lines.AddRange(outLines);
                data.Points.AddRange(outPoints);
                data.Polylines.AddRange(outPolylines);
                data.Splines.AddRange(outSplines);
                data.Texts.AddRange(outTexts);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                memoryManager.StopTransaction(value);
            }
        }

        private static T TransformEntity<T>(T entity, OdGeMatrix3d transform, out OdResult result) where T : OdDbEntity
        {
            T val = entity.clone() as T;
            result = val.transformBy(transform);
            return val;
        }

        public static IEnumerable<DLineType> GetLineTypes(this DbDatabase database)
        {
            OdDbLinetypeTable odDbLinetypeTable = database.Database.getLinetypeTableId().openObject() as OdDbLinetypeTable;
            OdDbSymbolTableIterator pIter = odDbLinetypeTable.newIterator();
            pIter.start();
            while (!pIter.done())
            {
                OdDbLinetypeTableRecord odDbLinetypeTableRecord = (OdDbLinetypeTableRecord)pIter.getRecordId().safeOpenObject();
                DLineType val = new DLineType();
                val.Id = (odDbLinetypeTableRecord.objectId().getHandle().ToUInt64());
                val.Definition = (odDbLinetypeTableRecord.comments());
                val.Name = (odDbLinetypeTableRecord.getName());
                yield return val;
                pIter.step();
            }
        }

        public static IEnumerable<DBlock> GetBlocks(this DbDatabase database)
        {
            OdDbBlockTable odDbBlockTable = database.Database.getBlockTableId().openObject() as OdDbBlockTable;
            OdDbSymbolTableIterator pIter = odDbBlockTable.newIterator();
            pIter.start();
            while (!pIter.done())
            {
                OdDbBlockTableRecord odDbBlockTableRecord = (OdDbBlockTableRecord)pIter.getRecordId().safeOpenObject();
                DBlock val = new DBlock();
                val.Id = (odDbBlockTableRecord.objectId().getHandle().ToUInt64());
                val.Name = (odDbBlockTableRecord.getName());
                yield return val;
                pIter.step();
            }
        }
    }
}